﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using SoSlam.Common.Communication.GameMessages;
using SoTools.Communicator;
using System.Collections.Generic;
using System.Linq;
using SoSlam.Common.Communication;
#if WINDOWS
using SoSlam.Common.ClientLogic.SoGameServiceReference;
using System.Threading;
#endif
#if WINDOWS_PHONE
using Microsoft.Phone.Reactive;
#endif

namespace SoSlam.Common.ClientLogic
{
    public class World : DrawableGameComponent
    {
        internal static Random Random = new Random();

        private Player _player;
        public Player Player { get { return _player; } }

        private Background _background;
        private TrackingCamera _camera;

        private Dictionary<int, Bird> _birds = new Dictionary<int, Bird>();
        private BirdsMessage _lastBirdsMessage;
        private BirdsMessage _previousBirdsMessage;

        private Dictionary<int, PowerUp> _powerups = new Dictionary<int, PowerUp>();
        private PowerupMessage _lastPowerupsMessage;
        private PowerupMessage _previousPowerupsMessage;
        private List<int> powerupCollected = new List<int>();

        private SpriteBatch _spriteBatch;
        private SpriteFont _normalTextFont;
        private Color _normalTextColor;

        private Texture2D _popupTexture;

        private Vector2 _position;
        public Vector2 Position { get { return _position; } }

        public GameStatus Status { get; private set; }

        private decimal? _score;

        private bool _displayFPS;

        private bool _sendScore;

        public World(Game game)
            : base(game)
        {
            _displayFPS = false;
        }

        public override void Initialize()
        {
            var communicator = (Communicator)Game.Services.GetService(typeof(Communicator));

            communicator.GenerateObservable<GameStatusMessage>().Subscribe(gsm =>
                                                                            {
                                                                                if (gsm.Status == GameStatus.WaitingForPlayer && !_player.IsReady)
                                                                                    return;

                                                                                Status = gsm.Status;

                                                                                if (Status != GameStatus.Ended)
                                                                                {
                                                                                    Reset();
                                                                                }
                                                                            });

            communicator.GenerateObservable<EndOfGameMessage>().Subscribe(gsm => _score = gsm.Score);
            communicator.GenerateObservable<BirdsMessage>().Subscribe(HandleBirds);
            communicator.GenerateObservable<PowerupMessage>().Subscribe(HandlePowerups);

            _spriteBatch = new SpriteBatch(Game.GraphicsDevice);

            this.DrawOrder = int.MaxValue - 10;

            _camera = new TrackingCamera(Game, this);
            Game.Components.Add(_camera);

            _background = new Background(Game, this, _camera);
            Game.Components.Add(_background);

            _player = new Player(Game, this, _camera);
            _player.DrawOrder = int.MaxValue;
            Game.Components.Add(_player);

            base.Initialize();
        }

        private void Reset()
        {
            _lastPowerupsMessage = null;
            _lastBirdsMessage = null;
            _score = null;
            _sendScore = false;

            foreach (var powerup in _powerups)
            {
                Game.Components.Remove(powerup.Value);
            }
            _powerups.Clear();

            foreach (var bird in _birds)
            {
                Game.Components.Remove(bird.Value);
            }
            _birds.Clear();
        }

        protected override void LoadContent()
        {
            _normalTextFont = Game.Content.Load<SpriteFont>("SpriteFonts/NormalText");
            _normalTextColor = Color.FromNonPremultiplied(153, 84, 29, 255);

            _popupTexture = Game.Content.Load<Texture2D>("Sprites/popup");

            base.LoadContent();
        }

        int frameRate = 0;
        int frameCounter = 0;
        TimeSpan elapsedTime = TimeSpan.Zero;

        public override void Update(GameTime gameTime)
        {
            #region FPS Counter

            if (_displayFPS)
            {
                // The time since Update was called last
                elapsedTime += gameTime.ElapsedGameTime;

                if (elapsedTime > TimeSpan.FromSeconds(1))
                {
                    elapsedTime -= TimeSpan.FromSeconds(1);
                    frameRate = frameCounter;
                    frameCounter = 0;
                }
            }

            #endregion

            UpdateBirds(gameTime);

            UpdatePowerups();

            base.Update(gameTime);
        }

        private void UpdateBirds(GameTime gameTime)
        {
            // Update from server
            if (_lastBirdsMessage != null && _previousBirdsMessage != _lastBirdsMessage)
            {
                _previousBirdsMessage = _lastBirdsMessage;

                // Update the bird list
                foreach (var birdMessage in _lastBirdsMessage.Birds)
                {
                    Bird bird = null;

                    if (!_birds.ContainsKey(birdMessage.BirdId))
                    {
                        if (birdMessage.X + _camera.Position.X + 50 > 0)
                        {
                            bird = new Bird(Game, this, _camera);
                            bird.DrawOrder = int.MaxValue - 10;

                            bird.BirdId = birdMessage.BirdId;

                            Game.Components.Add(bird);
                            _birds.Add(bird.BirdId, bird);
                        }
                    }
                    else
                    {
                        bird = _birds[birdMessage.BirdId];
                    }

                    if (bird != null)
                    {
                        // Smoothing
                        var serverPosition = new Vector2(birdMessage.X, -birdMessage.Y);
                        var positionDifference = serverPosition - bird.Position;
                        float distanceApart = positionDifference.Length();
                        if (distanceApart > Constants.SMOOTHING_MAX_DISTANCE)
                            bird.Position = serverPosition;
                        else if (distanceApart > Constants.SMOOTHING_MIN_DISTANCE)
                            bird.Position += positionDifference * 0.1f;

                        //bird.Position = new Vector2((float)Math.Round(birdMessage.X), (float)Math.Round(-birdMessage.Y));
                        bird.IsHit = birdMessage.IsHit;
                    }
                }

                // Cleaning if disappears from the server list or from the screen
                var birdsToRemove = new List<int>();
                foreach (var bird in _birds)
                {
                    if (!_lastBirdsMessage.Birds.Any(b => b.BirdId == bird.Key)
                        || bird.Value.Position.X + bird.Value.Width + _camera.Position.X + 50 < 0)
                    {
                        birdsToRemove.Add(bird.Key);
                    }
                }
                foreach (var birdToRemove in birdsToRemove)
                {
                    Game.Components.Remove(_birds[birdToRemove]);
                    _birds.Remove(birdToRemove);
                }
            }
            else // Update with predictions from client
            {
                foreach (var bird in _birds.Values)
                {
                    bird.Position = bird.Position + new Vector2(Constants.BIRD_SPEED_X, 0) * Constants.TimeAdjustmentFactor * (float)gameTime.ElapsedGameTime.TotalSeconds;
                }
            }
        }

        private void UpdatePowerups()
        {
            // Update from server
            if (_lastPowerupsMessage != null && _previousPowerupsMessage != _lastPowerupsMessage)
            {
                _previousPowerupsMessage = _lastPowerupsMessage;

                // Update the powerups list
                foreach (var powerupMessage in _lastPowerupsMessage.Powerups)
                {
                    PowerUp powerup = null;

                    if (!_powerups.ContainsKey(powerupMessage.PowerupId))
                    {
                        if (powerupMessage.X + _camera.Position.X + 50 > 0)
                        {
                            powerup = new PowerUp(Game, this, _camera);
                            powerup.DrawOrder = int.MaxValue - 20;

                            powerup.Type = powerupMessage.Type;
                            powerup.PowerupId = powerupMessage.PowerupId;

                            // Position only updated once, a powerup doesn't move !
                            powerup.Position = new Vector2((float)Math.Round(powerupMessage.X), (float)Math.Round(-powerupMessage.Y));

                            Game.Components.Add(powerup);
                            _powerups.Add(powerup.PowerupId, powerup);
                        }
                    }
                    else
                    {
                        powerup = _powerups[powerupMessage.PowerupId];
                    }

                    if (powerup != null)
                    {
                        powerup.IsHit = powerupMessage.IsHit;
                        if (powerup.IsHit && !powerupCollected.Contains(powerup.PowerupId))
                        {
                            powerupCollected.Add(powerup.PowerupId);
                            _player.PowerupCollision(powerup);
                        }
                    }
                }

                // Cleaning if disappears from the server list or from the screen
                var powerupsToRemove = new List<int>();
                foreach (var powerup in _powerups)
                {
                    if (!_lastPowerupsMessage.Powerups.Any(p => p.PowerupId == powerup.Key)
                        || powerup.Value.Position.X + powerup.Value.Width + _camera.Position.X + 50 < 0)
                    {
                        powerupsToRemove.Add(powerup.Key);
                    }
                }
                foreach (var pToRemove in powerupsToRemove)
                {
                    Game.Components.Remove(_powerups[pToRemove]);
                    _powerups.Remove(pToRemove);
                }
            }
        }

        public override void Draw(GameTime gameTime)
        {
            _spriteBatch.Begin();

            if (_displayFPS)
            {
                frameCounter++;

                _spriteBatch.DrawString(_normalTextFont, string.Format("{0} fps", frameRate), new Vector2(5, 0), Color.White);
            }

            Vector2 center = new Vector2(GraphicsDevice.Viewport.Width / 2.0f,
                                         GraphicsDevice.Viewport.Height / 2.0f);

            if (Status != GameStatus.Running)
            {
                _spriteBatch.Draw(
                    _popupTexture,
                    center - new Vector2(_popupTexture.Width / 2, _popupTexture.Height / 2),
                    Color.White);
            }

            switch (Status)
            {
                case GameStatus.Initial:
                    _spriteBatch.DrawCenteredString(
                        _normalTextFont,
                        "Pret ?",
                        center,
                        _normalTextColor);
                    break;
                case GameStatus.WaitingForPlayer:
                    _spriteBatch.DrawCenteredString(
                        _normalTextFont,
                        "En attente",
                        center,
                        _normalTextColor);
                    _spriteBatch.DrawCenteredString(
                        _normalTextFont,
                        "d'un joueur...",
                        center + new Vector2(0,25),
                        _normalTextColor);
                    break;
                case GameStatus.Running:
                    break;
                case GameStatus.Paused:
                    _spriteBatch.DrawCenteredString(
                        _normalTextFont,
                        "Pause",
                        center,
                        _normalTextColor);
                    break;
                case GameStatus.Ended:
                    _spriteBatch.DrawCenteredString(
                        _normalTextFont,
                        "Fini !",
                        center - new Vector2(0, 10),
                        _normalTextColor);

                    if (_score != null)
                    {
                        _spriteBatch.DrawCenteredString(
                            _normalTextFont,
                            String.Format("Score :  {0:0}", _score),
                            center + new Vector2(0, 25),
                            _normalTextColor);

#if WINDOWS
                        //Envoi de la partie à la BDD
                        if (!_sendScore && _score != null && _score > 100)
                        {
                            _sendScore = true;

                            ThreadPool.QueueUserWorkItem(new WaitCallback(state =>
                                {
                                    try
                                    {
                                        SoGameClient client = new SoGameClient();
                                        client.SaveGamePlayFromNetwork("SoSlam", Convert.ToInt32((decimal)_score), _player.PhotoFileName);
                                    }
                                    catch
                                    {

                                    }
                                }

                            ));
                        }
#endif
                    }
                    break;
            }

            _spriteBatch.End();

            base.Draw(gameTime);
        }

        private void HandleBirds(BirdsMessage msg)
        {
            _lastBirdsMessage = msg;
        }

        private void HandlePowerups(PowerupMessage msg)
        {
            _lastPowerupsMessage = msg;
        }
    }
}
